package cn.vehicle.biz.eventListener;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.vehicle.biz.event.GIoInResultEvent;
import cn.vehicle.constants.Constants;
import cn.vehicle.domains.ctx.PlateDetectContext;
import cn.vehicle.domains.entity.CameraEntity;
import cn.vehicle.domains.entity.VehicleTransportLedgerEntity;
import cn.vehicle.enums.GIoInTypeEnum;
import cn.vehicle.enums.InOutTypeEnum;
import cn.vehicle.enums.VehicleTypeEnum;
import cn.vehicle.exception.VehicleAccessException;
import cn.vehicle.service.CameraService;
import cn.vehicle.service.VehicleTransportLedgerService;
import cn.vehicle.service.config.SystemConfig;
import cn.vehicle.service.config.SystemConfigService;
import cn.vehicle.utils.NvrUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.util.Date;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * GIO 输入结果视频截取监听器
 * <p>
 * 监听 {@link GIoInResultEvent} 事件，在 GPIO 输入信号触发后执行视频截取操作。
 * 主要用于运输车辆出入厂时，自动从 NVR 中截取相应时间段的视频片段（例如车辆经过、抬杆场景）。
 * </p>
 *
 * <p>主要职责：</p>
 * <ul>
 *   <li>监听 GIO 输入事件，判断是否为有效触发信号；</li>
 *   <li>根据识别上下文计算视频截取时间段；</li>
 *   <li>调用 FFmpeg 从 NVR 拉取视频流并保存到本地文件；</li>
 *   <li>将截取到的视频路径更新至运输车辆台账记录。</li>
 * </ul>
 */
@Component
@Slf4j
public class GIoInResultCutVideoListener implements ApplicationListener<GIoInResultEvent> {

    @Resource
    private CameraService cameraService;

    @Resource
    private NvrUtil nvrUtil;

    @Resource
    private SystemConfigService systemConfigService;

    @Resource
    private VehicleTransportLedgerService vehicleTransportLedgerService;

    ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(2);


    /**
     * 监听并处理 GIO 输入结果事件
     * <p>
     * 当接收到 {@link GIoInResultEvent} 时，根据车辆类型、GPIO 信号类型及系统配置，
     * 计算需要截取的时间范围，并延时执行视频截取任务。
     * </p>
     *
     * @param event GIO 输入结果事件对象，包含车辆识别上下文信息
     */
    @Override
    public void onApplicationEvent(GIoInResultEvent event) {
        // 从事件中获取车辆识别上下文信息
        PlateDetectContext plateDetectContext = event.getPlateDetectContext();

        // 仅对运输车辆类型进行视频截取处理，其他类型直接返回
        if (!VehicleTypeEnum.TRANSPORT.name().equals(plateDetectContext.getVehicleType())) {
            return;
        }

        // 如果 GPIO 输入类型为“上升沿（UP）”，表示信号开始，不执行视频截取
        if (plateDetectContext.getGIoInType() == GIoInTypeEnum.UP) {
            return;
        }

        // 获取与当前地感相机绑定的高位摄像头，用于视频截取
        CameraEntity snapCameraEntity = cameraService.getCameraById(plateDetectContext.getCameraEntity().getAssociationCameraId());
        if (snapCameraEntity == null) {
            throw VehicleAccessException.DATE_NOT_EXIST.newInstance("获取高位相机失败 获取结果为空");
        }

        // 生成视频文件保存路径，文件名使用 UUID 确保唯一
        String filePath = String.join(File.separator, Constants.VIDEO_FILE_PATH , IdUtil.simpleUUID() + ".mp4");

        final File dest = new File(filePath);
        final File parentFile = dest.getParentFile();
        if (!parentFile.exists()) {
            // 若父目录不存在，则递归创建目录
            try {
                Files.createDirectories(parentFile.toPath());
            } catch (IOException e) {
                log.error("[NvrStreamRecorder record] createDirectories error ");
            }
        }

        // 获取系统配置中的延迟时间与截取间隔时间
        Integer delayTime = systemConfigService.get(SystemConfig.cutVideoDelayTime);
        Integer intervalTime = systemConfigService.get(SystemConfig.cutVideoDelayTime);

        // 根据上下文中的上下闸时间计算视频截取的起止时间段
        DateTime startTime = DateUtil.offset(DateUtil.offset(plateDetectContext.getUpTime(), DateField.SECOND, delayTime), DateField.SECOND, -intervalTime);
        DateTime endTime = DateUtil.offset(DateUtil.offset(plateDetectContext.getDownTime(), DateField.SECOND, delayTime), DateField.SECOND, intervalTime);

        // 延时 30 秒后执行视频录制任务，避免网络延迟或缓存未生成问题
        scheduler.schedule(() -> record(filePath, snapCameraEntity.getChannelCode(), snapCameraEntity.getNvrAddressUrl(), startTime, endTime),
                30, TimeUnit.SECONDS);

        // 创建运输车辆台账更新对象，更新进/出视频文件路径
        VehicleTransportLedgerEntity updateEntity = new VehicleTransportLedgerEntity();
        updateEntity.setId(plateDetectContext.getVehicleTransportLedgerEntity().getId());

        // 根据进出类型设置不同的视频路径字段
        if (InOutTypeEnum.IN.name().equals(plateDetectContext.getInOutType())) {
            updateEntity.setEntryVideoUrl(filePath);
        } else {
            updateEntity.setExitVideoUrl(filePath);
        }

        // 更新运输车辆的台账信息，保存视频文件路径
        vehicleTransportLedgerService.updateTransportExitInfo(updateEntity);

    }


    /**
     * 执行视频截取操作
     * <p>
     * 调用 FFmpeg 从 NVR 中拉取指定时间段的 RTSP 视频流，并保存为本地文件。
     * </p>
     *
     * @param outputFilePath 视频输出文件路径
     * @param channelCode NVR 通道号
     * @param nvrAddressUrl NVR 地址
     * @param startTime 截取开始时间
     * @param endTime 截取结束时间
     */
    public void record(String outputFilePath, String channelCode, String nvrAddressUrl, Date startTime, Date endTime) {

        // 计算截取时长（秒）
        long cutVideoSecond = DateUtil.between(startTime, endTime, DateUnit.SECOND);

        // 格式化起止时间为 NVR 兼容的格式（yyyyMMdd't'HHmmss'z'）
        String startTimeStr = DateUtil.format(startTime, "yyyyMMdd't'HHmmss'z'");
        String endTimeStr = DateUtil.format(endTime, "yyyyMMdd't'HHmmss'z'");

        // 调用 NVR 工具类生成 RTSP 播放地址（支持回放模式）
        final String nvrRtspUrl = nvrUtil.getNvrUrl(startTimeStr, endTimeStr, nvrAddressUrl, channelCode);

        // 拼接 FFmpeg 命令行，用于截取视频片段
        String ffmpegCommand = String.format(
                //"ffmpeg -sseof -%d -i %s -c:v copy -c:a aac -strict experimental -t %d %s",
                "ffmpeg -i \"%s\" -y -c:v copy -an -t %d %s",
                nvrRtspUrl, cutVideoSecond, outputFilePath
        );

        log.info("ffmpegCommand :{}", ffmpegCommand);
        Process process = null;
        BufferedReader reader = null;
        try {
            // 创建并启动 FFmpeg 进程执行视频截取任务
            ProcessBuilder processBuilder = new ProcessBuilder(ffmpegCommand.split(" "));
            processBuilder.redirectErrorStream(true);
            process = processBuilder.start();

            // 读取并打印FFmpeg的输出
            reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                // 循环读取 FFmpeg 的控制台输出日志，方便排查问题
                log.info(line);
            }

            // 等待进程完成
            process.waitFor();
            log.info("[NvrStreamRecorder record] success outputFilePath:{}" , outputFilePath);
        } catch (Exception e) {
            log.error("[NvrStreamRecorder record]Error executing FFmpeg command:{}", ffmpegCommand , e);
        } finally {
            // 关闭流和销毁进程，释放系统资源
            try {
                if (reader != null){
                    reader.close();
                }
            } catch (IOException e) {
                log.error("[NvrStreamRecorder record] close reader error ");
            }
            if (process != null){
                process.destroy();
            }

        }
    }
}
